package com.allinpay.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.http.HttpUtil;
import com.allinpay.common.SybUtil;
import com.allinpay.pojo.Allinpay;
import com.allinpay.pojo.PayConfig;
import com.allinpay.pojo.PayLoad;
import com.allinpay.service.PayService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TreeMap;

@Service
@Slf4j
public class AllinpayServiceImpl implements PayService {

    // 支付接口
    private final String order = "https://syb.allinpay.com/apiweb/h5unionpay/unionorder";

    // 查询接口
    private final String query = "https://vsp.allinpay.com/apiweb/tranx/query";

    // 关闭接口
    private final String close = "https://vsp.allinpay.com/apiweb/tranx/close";

    // 取消当天交易退款接口
    private final String cancelDayUrl = "https://vsp.allinpay.com/apiweb/tranx/cancel";

    // 退款接口
    private final String refundUrl = "https://vsp.allinpay.com/apiweb/tranx/refund";

    private final String rsaPrivateKey = "xxxxx";

    @SneakyThrows
    @Override
    public String h5Pay(PayLoad payLoad) {
        // String payConfigKey = payLoad.getPayConfigKey();
        PayConfig payConfig = new PayConfig()
                .setAppid("xxx")
                .setCusid("xxxx")
                .setPrivateKey(rsaPrivateKey);
        Allinpay allinpay = new Allinpay()
                .setSigntype("RSA")
                .setTrxamt(new BigDecimal(2))
                .setReqsn(payLoad.getOrderId())
                .setRandomstr(SybUtil.getValidatecode(8))
                .setBody(payLoad.getTitle())
                .setRemark(payLoad.getRemark())
                .setCharset("UTF-8")
                .setAppid(payConfig.getAppid())
                .setCusid(payConfig.getCusid())
                .setReturl(payLoad.getReturnUrl());
        allinpay.setSign(URLEncoder.encode(SybUtil.unionSign(objectToTreeMap(allinpay), payConfig.getPrivateKey() , "RSA"), StandardCharsets.UTF_8));
        return order + "?" + treeMapToUrlParams(objectToTreeMap(allinpay));
    }

    @SneakyThrows
    @Override
    public String closeOrder(PayLoad payLoad) {
        PayConfig payConfig = new PayConfig()
                .setAppid("xxx")
                .setCusid("xxxx")
                .setPrivateKey(rsaPrivateKey);
        Allinpay allinpay = new Allinpay()
                .setCusid(payConfig.getCusid())
                .setAppid(payConfig.getAppid())
                .setRandomstr(SybUtil.getValidatecode(8))
                .setVersion(11)
                .setOldreqsn(payLoad.getOrderId())
                .setSigntype("RSA");
        allinpay.setSign(SybUtil.unionSign(objectToTreeMap(allinpay), rsaPrivateKey, "RSA"));
        return HttpUtil.post(close, BeanUtil.beanToMap(allinpay));
    }

    @SneakyThrows
    @Override
    public String query(PayLoad payLoad) {
        PayConfig payConfig = new PayConfig()
                .setAppid("xxx")
                .setCusid("xxxx")
                .setPrivateKey(rsaPrivateKey);
        Allinpay allinpay = new Allinpay()
                .setCusid(payConfig.getCusid())
                .setAppid(payConfig.getAppid())
                .setRandomstr(SybUtil.getValidatecode(8))
                .setVersion(11)
                .setReqsn(payLoad.getOrderId())
                .setSigntype("RSA");
        allinpay.setSign(SybUtil.unionSign(objectToTreeMap(allinpay), rsaPrivateKey, "RSA"));
        return HttpUtil.post(query, BeanUtil.beanToMap(allinpay));
    }

    @SneakyThrows
    @Override
    public String cancelDay(PayLoad payLoad) {
        PayConfig payConfig = new PayConfig()
                .setAppid("xxx")
                .setCusid("xxxx")
                .setPrivateKey(rsaPrivateKey);
        Allinpay allinpay = new Allinpay()
                .setCusid(payConfig.getCusid())
                .setAppid(payConfig.getAppid())
                .setRandomstr(SybUtil.getValidatecode(8))
                .setVersion(11)
                .setTrxamt(new BigDecimal(2))
                .setReqsn(payLoad.getOrderId())
                .setOldreqsn(payLoad.getOrderId())
                .setSigntype("RSA");
        allinpay.setSign(SybUtil.unionSign(objectToTreeMap(allinpay), rsaPrivateKey, "RSA"));
        return HttpUtil.post(cancelDayUrl, BeanUtil.beanToMap(allinpay));
    }

    @SneakyThrows
    @Override
    public String refund(PayLoad payLoad) {
        PayConfig payConfig = new PayConfig()
                .setAppid("xxx")
                .setCusid("xxxx")
                .setPrivateKey(rsaPrivateKey);
        Allinpay allinpay = new Allinpay()
                .setCusid(payConfig.getCusid())
                .setAppid(payConfig.getAppid())
                .setRandomstr(SybUtil.getValidatecode(8))
                .setVersion(11)
                .setTrxamt(new BigDecimal(2))
                .setReqsn(payLoad.getOrderId())
                .setOldreqsn(payLoad.getOrderId())
                .setSigntype("RSA");
        allinpay.setSign(SybUtil.unionSign(objectToTreeMap(allinpay), rsaPrivateKey, "RSA"));
        return HttpUtil.post(refundUrl, BeanUtil.beanToMap(allinpay));
    }


    private static String treeMapToUrlParams(TreeMap<String, String> treeMap) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : treeMap.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (sb.length() > 0) {
                sb.append("&");
            }
            sb.append(key).append("=").append(value);
        }
        return sb.toString();
    }


    private TreeMap<String, String> objectToTreeMap(Object obj) {
        TreeMap<String, String> treeMap = new TreeMap<>();
        Class<?> clazz = obj.getClass();
        while (clazz != null) {
            for (Field field : clazz.getDeclaredFields()) {
                field.setAccessible(true);
                try {
                    Object fieldValue = field.get(obj);
                    if (fieldValue != null) {
                        treeMap.put(field.getName(), fieldValue.toString());
                    }
                } catch (IllegalAccessException e) {
                    log.error("Error accessing field " + field.getName() + ": " + e.getMessage());
                }
            }
            clazz = clazz.getSuperclass();
        }
        return treeMap;
    }



}
